package org.imitation.demo;

import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

public class WordFrequencyUtil {

    /**
     * Calculate the word frequency of a text.
     * The text splits on whitespace.
     * The compare ignores case.
     * <pre>
     * WordFrequencyUtil.countFrequency(null)                    = null
     * WordFrequencyUtil.countFrequency("")                      = {}
     * WordFrequencyUtil.countFrequency("Ab cd  ab")             = {"ab": 2, "cd": 1}
     * </pre>
     * @param text the String to be counted, may be null.
     * @return a {@link Map} that present the counting result,
     * whose key is the calculated word, and its corresponding value is the frequency of word occurrence.
     */
    public static Map<String, Integer> countFrequency(final String text) {
        return countFrequency(text, null, true);
    }

    /**
     * Calculate the word frequency of a text.
     * The compare ignores case.
     * <pre>
     * WordFrequencyUtil.countFrequency(null, *)              = null
     * WordFrequencyUtil.countFrequency("", *)                = {}
     * WordFrequencyUtil.countFrequency("Ab cd  ab", null) = {"ab": 2, "cd": 1}
     * WordFrequencyUtil.countFrequency("Ab,cd, ab", null) = {"ab,cd,": 1, "ab": 1}
     * WordFrequencyUtil.countFrequency("Ab,cd, ab", ",")  = {"ab": 1, "cd": 1, " ab": 1}
     * WordFrequencyUtil.countFrequency("Ab,cd, ab", " ,") = {"ab": 2, "cd": 1}
     * </pre>
     * @param text the String to be counted, may be null.
     * @param separatorChars the characters used as the delimiters,
     *  {@code null} splits on whitespace.
     * @return a {@link Map} that present the counting result,
     * whose key is the calculated word, and its corresponding value is the frequency of word occurrence.
     */
    public static Map<String, Integer> countFrequency(final String text, final String separatorChars) {
        return countFrequency(text, separatorChars, true);
    }


    /**
     * Calculate the word frequency of a text.
     * The text splits on whitespace.
     * <pre>
     * WordFrequencyUtil.countFrequency(null, *)              = null
     * WordFrequencyUtil.countFrequency("", *)                = {}
     * WordFrequencyUtil.countFrequency("Ab cd  ab", true) = {"ab": 2, "cd": 1}
     * WordFrequencyUtil.countFrequency("Ab,cd, ab", true) = {"ab,cd,": 1, "ab": 1}
     * WordFrequencyUtil.countFrequency("Ab cd  ab", false)= {"Ab": 1, "cd": 1, "ab": 1}
     * </pre>
     * @param text the String to be counted, may be null.
     * @param ignoreCase indicates whether the compare should ignore case
     *  (case-insensitive) or not.
     * @return a {@link Map} that present the counting result,
     * whose key is the calculated word, and its corresponding value is the frequency of word occurrence.
     */
    public static Map<String, Integer> countFrequency(final String text, final boolean ignoreCase) {
        return countFrequency(text, null, ignoreCase);
    }

    /**
     * Calculate the word frequency of a text.
     * <pre>
     * WordFrequencyUtil.countFrequency(null, *, *)              = null
     * WordFrequencyUtil.countFrequency("", *, *)                = {}
     * WordFrequencyUtil.countFrequency("Ab cd  ab", null, true) = {"ab": 2, "cd": 1}
     * WordFrequencyUtil.countFrequency("Ab,cd, ab", null, true) = {"ab,cd,": 1, "ab": 1}
     * WordFrequencyUtil.countFrequency("Ab,cd, ab", ",", true)  = {"ab": 1, "cd": 1, " ab": 1}
     * WordFrequencyUtil.countFrequency("Ab,cd, ab", " ,", true) = {"ab": 2, "cd": 1}
     * WordFrequencyUtil.countFrequency("Ab cd  ab", null, false)= {"Ab": 1, "cd": 1, "ab": 1}
     * </pre>
     * @param text the String to be counted, may be null.
     * @param separatorChars the characters used as the delimiters,
     *  {@code null} splits on whitespace.
     * @param ignoreCase indicates whether the compare should ignore case
     *  (case-insensitive) or not.
     * @return a {@link Map} that present the counting result,
     * whose key is the calculated word, and its corresponding value is the frequency of word occurrence.
     */
    public static Map<String, Integer> countFrequency(final String text, final String separatorChars, final boolean ignoreCase) {
        return countFrequencyWorker(text, separatorChars, ignoreCase);
    }

    /**
     * Calculate the word frequency of a text.
     * @param text the String to be counted, may be null.
     * @param separatorChars the characters used as the delimiters,
     *  {@code null} splits on whitespace.
     * @param ignoreCase indicates whether the compare should ignore case
     *  (case-insensitive) or not.
     * @return a {@link Map} that present the counting result,
     * whose key is the calculated word, and its corresponding value is the frequency of word occurrence.
     */
    private static Map<String, Integer> countFrequencyWorker(final String text, final String separatorChars, final boolean ignoreCase) {
        if (text == null) {
            return null;
        }
        Map<String, Integer> wordFrequencyMap = new HashMap<>();
        String[] words = StringUtils.split(text, separatorChars);
        for (String word : words) {
            if (ignoreCase) {
                word = word.toLowerCase();
            }
            if (!word.isEmpty()) {
                int frequency = wordFrequencyMap.getOrDefault(word, 0);
                wordFrequencyMap.put(word, frequency + 1);
            }
        }
        return wordFrequencyMap;
    }

}
